/*** 
 * @Author: 王耀泽
 * @Date: 2025-06-23 15:02:17
 * @LastEditTime: 2025-06-23 15:02:17
 * @LastEditors: aigis
 * @Description: 15201563666
 * @FilePath: \vue-cesium2\src\minxi\cl\measureAreaSpace.js
 * @先看《地理信息导论第七版》
 */
import * as Cesium from 'cesium'
class AreaSpaceMeasurement {
  constructor(arg) {
    if (!arg.viewer) throw new Error('Viewer 实例必须提供');

    this.viewer = arg.viewer;
    this.Cesium = arg.Cesium;
    if (!this.Cesium) {
      if (typeof Cesium === 'undefined') {
        throw new Error('Cesium 库未正确加载，请检查 HTML 文件中是否引入了 Cesium 库。');
      }
      this.Cesium = Cesium;
    }
    this.areaHandler = null;
    this.positions = [];
    this.tempPoints = [];
    this.polygon = null;
    this.areaText = null;
    this.pointEntities = [];
    this.isActive = false;
    this.radiansPerDegree = Math.PI / 180.0;
    this.degreesPerRadian = 180.0 / Math.PI;
  }

  /**
   * 开始空间面积测量
   */
  start() {
    if (this.isActive) return;
    this.clear();
    this.isActive = true;
    if (this.viewer.cesiumWidget) {
      this.viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
        this.Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
      );
    }
    // 取消双击事件


    this.areaHandler = new this.Cesium.ScreenSpaceEventHandler(
      this.viewer.scene._imageryLayerCollection
    );

    // 鼠标移动事件
    this.areaHandler.setInputAction((movement) => {
      const cartesian = this._getCartesianFromPosition(movement.endPosition);
      if (!cartesian) return;

      if (this.positions.length >= 2) {
        if (!this.polygon) {
          this.polygon = new PolygonPrimitive(this.positions, this.viewer, this.Cesium);
        } else {
          this.positions.pop();
          this.positions.push(cartesian);
        }
      }
    }, this.Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    // 左键点击事件
    this.areaHandler.setInputAction((movement) => {
      const cartesian = this._getCartesianFromPosition(movement.position);
      if (!cartesian) return;

      if (this.positions.length === 0) {
        this.positions.push(cartesian.clone());
      }

      this.positions.push(cartesian);

      // 添加点实体
      const cartographic = this.Cesium.Cartographic.fromCartesian(cartesian);
      const pointEntity = this._createPointEntity(cartesian);
      this.pointEntities.push(pointEntity);

      // 记录经纬度和高度
      this.tempPoints.push({
        lon: this.Cesium.Math.toDegrees(cartographic.longitude),
        lat: this.Cesium.Math.toDegrees(cartographic.latitude),
        hei: cartographic.height
      });
    }, this.Cesium.ScreenSpaceEventType.LEFT_CLICK);

    // 右键点击事件 - 结束测量并计算面积
    this.areaHandler.setInputAction(() => {
      this._finishMeasurement();
    }, this.Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

  /**
   * 完成测量并计算面积
   * @private
   */
  _finishMeasurement() {
    if (!this.isActive) return;

    this.areaHandler.destroy();
    this.areaHandler = null;

    if (this.positions.length > 0) {
      this.positions.pop(); // 移除最后一个无效点
    }

    if (this.tempPoints.length > 0) {
      this.tempPoints.pop(); // 移除最后一个无效点
    }

    // 计算面积
    const area = this._calculateArea(this.tempPoints);
    const areaText = `${area} 平方公里`;

    // 添加面积标签
    if (this.positions.length > 0) {
      this.areaText = this._createAreaLabel(
        this.positions[this.positions.length - 1],
        areaText
      );
      this.pointEntities.push(this.areaText);
    }

    this.isActive = false;
  }

  /**
   * 清除测量结果
   */
  clear() {
    this.isActive = false;

    // 移除所有实体
    this.pointEntities.forEach(entity => {
      if (this.viewer.entities.contains(entity)) {
        this.viewer.entities.remove(entity);
      }
    });

    if (this.polygon) {
      this.polygon.destroy();
      this.polygon = null;
    }

    this.positions = [];
    this.tempPoints = [];
    this.areaText = null;
    this.pointEntities = [];
  }

  /**
   * 从屏幕位置获取地形坐标
   * @private
   */
  _getCartesianFromPosition(position) {
    const ray = this.viewer.camera.getPickRay(position);
    return this.viewer.scene.globe.pick(ray, this.viewer.scene);
  }

  /**
   * 创建点实体
   * @private
   */
  _createPointEntity(position) {
    return this.viewer.entities.add({
      name: '多边形面积测量点',
      position: position,
      point: {
        pixelSize: 5,
        color: this.Cesium.Color.RED,
        outlineColor: this.Cesium.Color.WHITE,
        outlineWidth: 2,
        heightReference: this.Cesium.HeightReference.CLAMP_TO_GROUND
      }
    });
  }

  /**
   * 创建面积标签
   * @private
   */
  _createAreaLabel(position, text) {
    return this.viewer.entities.add({
      name: '多边形面积',
      position: position,
      label: {
        text: text,
        font: '18px sans-serif',
        fillColor: this.Cesium.Color.GOLD,
        style: this.Cesium.LabelStyle.FILL_AND_OUTLINE,
        outlineWidth: 2,
        verticalOrigin: this.Cesium.VerticalOrigin.BOTTOM,
        pixelOffset: new this.Cesium.Cartesian2(20, -40),
        heightReference: this.Cesium.HeightReference.CLAMP_TO_GROUND
      }
    });
  }

  /**
   * 计算多边形面积
   * @private
   */
  _calculateArea(points) {
    let area = 0;

    // 拆分三角曲面计算面积
    for (let i = 0; i < points.length - 2; i++) {
      const j = (i + 1) % points.length;
      const k = (i + 2) % points.length;
      const totalAngle = this._calculateAngle(points[i], points[j], points[k]);
      const distance1 = this._calculateDistance(
        this._getCartesianFromLonLatHeight(points[i]),
        this._getCartesianFromLonLatHeight(points[j])
      );
      const distance2 = this._calculateDistance(
        this._getCartesianFromLonLatHeight(points[j]),
        this._getCartesianFromLonLatHeight(points[k])
      );

      area += distance1 * distance2 * Math.abs(Math.sin(totalAngle));
    }

    // 转换为平方公里
    return (area / 1000000.0).toFixed(4);
  }

  /**
   * 计算角度
   * @private
   */
  _calculateAngle(p1, p2, p3) {
    const bearing21 = this._calculateBearing(p2, p1);
    const bearing23 = this._calculateBearing(p2, p3);
    let angle = bearing21 - bearing23;

    if (angle < 0) {
      angle += 360;
    }

    return angle * this.radiansPerDegree; // 转换为弧度
  }

  /**
   * 计算方向
   * @private
   */
  _calculateBearing(from, to) {
    const lat1 = from.lat * this.radiansPerDegree;
    const lon1 = from.lon * this.radiansPerDegree;
    const lat2 = to.lat * this.radiansPerDegree;
    const lon2 = to.lon * this.radiansPerDegree;

    let angle = -Math.atan2(
      Math.sin(lon1 - lon2) * Math.cos(lat2),
      Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2)
    );

    if (angle < 0) {
      angle += Math.PI * 2.0;
    }

    return angle * this.degreesPerRadian; // 转换为角度
  }

  /**
   * 计算两点之间的距离
   * @private
   */
  _calculateDistance(point1, point2) {
    const point1Cartographic = this.Cesium.Cartographic.fromCartesian(point1);
    const point2Cartographic = this.Cesium.Cartographic.fromCartesian(point2);

    const geodesic = new this.Cesium.EllipsoidGeodesic();
    geodesic.setEndPoints(point1Cartographic, point2Cartographic);
    let distance = geodesic.surfaceDistance;

    // 考虑高度差的三维距离
    distance = Math.sqrt(
      Math.pow(distance, 2) +
      Math.pow(point2Cartographic.height - point1Cartographic.height, 2)
    );

    return distance;
  }

  /**
   * 从经纬度和高度获取笛卡尔坐标
   * @private
   */
  _getCartesianFromLonLatHeight(point) {
    const cartographic = this.Cesium.Cartographic.fromDegrees(
      point.lon,
      point.lat,
      point.hei
    );
    return this.Cesium.Cartesian3.fromRadians(
      cartographic.longitude,
      cartographic.latitude,
      cartographic.height
    );
  }
}

/**
 * 辅助类：动态更新的多边形
 */
class PolygonPrimitive {
  constructor(positions, viewer, Cesium) {
    this.positions = positions;
    this.viewer = viewer;
    this.Cesium = Cesium;
    this.entity = null;
    this._init();
  }

  _init() {
    const updateHierarchy = () => ({ positions: this.positions });

    this.entity = this.viewer.entities.add({
      name: '多边形面积测量',
      polygon: {
        hierarchy: new this.Cesium.CallbackProperty(updateHierarchy, false),
        material: this.Cesium.Color.GREEN.withAlpha(0.5),
        outline: true,
        outlineColor: this.Cesium.Color.WHITE,
        heightReference: this.Cesium.HeightReference.CLAMP_TO_GROUND
      }
    });
  }

  destroy() {
    if (this.entity) {
      this.viewer.entities.remove(this.entity);
      this.entity = null;
    }
  }
}

export default AreaSpaceMeasurement;